﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GPforRobocode
{
    class GP
    {
        const int POPULATION_SIZE = 100;
        const int GENERATION_SIZE = 1000;
        const int INIT_DEPTH = 3;
        public const int MAXDEPTH = 10;
        public const int MT_R = 3;
        int _gen;
        int BEST;
        static public Random _rand = new Random();
        List<SolutionForThreeTree> _population;
        List<SolutionForThreeTree> _spring;
        SolutionForThreeTree _bestSol;
        public SolutionForThreeTree GetAnswer {
            get { return _bestSol; }        
        } 
        public GP() {
            RobotTool.ERC = -1 + _rand.NextDouble() * 2;
            BEST = 0;
            _gen = 0;
            _spring = new List<SolutionForThreeTree>();
            _population = new List<SolutionForThreeTree>();
            /*
            Solution S1 = Read.ReadString("Div(ArcSin(Heading)Neg(EnemyEnergy))");
            Solution S2 = Read.ReadString("Sin(Firing(Y))");
            Solution S3 = Read.ReadString("Mul(Firing(Y)Abs(EnemyEnergy))");
            SolutionForThreeTree GoodSol = new SolutionForThreeTree(S1,S2,S3);
            GoodSol.Eval();
            _population.Add(GoodSol);
            */

            for (int i = 0; i < POPULATION_SIZE; i++) {
                SolutionForThreeTree temp = new SolutionForThreeTree(INIT_DEPTH);
                //temp.Eval();
                _population.Add(temp);

            }
            _bestSol = _population[0];
        }

        public void Run() {
            while (!Stop())
            {
                for (int i = 0; i < POPULATION_SIZE / 4; i++)
                {
                    int fa=0, ma=0;
                    Selection(ref fa,ref ma);
                    CrossOver(fa, ma);
                    Console.WriteLine(_gen + " " + i+" cr");
                }

                for (int i = 0; i < POPULATION_SIZE; i++)
                {
                    if (i == BEST)
                    {
                        continue;
                    }
                    Mutation(_population[i]);
                    Console.WriteLine(_gen + " " + i + " mt");
                }
                Survive();               
            }
            CheckBestSol();
        }
        bool Stop() {
            ++_gen;
            return (_gen > GENERATION_SIZE);
        }
        void Selection(ref int fa,ref int ma){
            // n tournament
            int n = 4;
            fa = _rand.Next(POPULATION_SIZE);
            ma = _rand.Next(POPULATION_SIZE);
            int R = 33;

            if (_rand.Next(100) < R)
            {
                ma = BEST;
            }
            if (_population[fa].Score > _population[ma].Score)
            {
                int temp = ma;
                ma = fa;
                fa = temp;

            }
            for (int i = 2; i < n; i++)
            {
                int temp = _rand.Next(POPULATION_SIZE);
                if (_population[temp].Score < _population[fa].Score)
                {
                    ma = fa;
                    fa = temp;
                }
                else if (_population[temp].Score < _population[ma].Score)
                {
                    ma = temp;
                }
            }
        }
        void CrossOver(int fa,int ma){
            if (_population[fa].IsTooBig() || _population[ma].IsTooBig()||fa==ma) {
                return;
            }
            SolutionForThreeTree NewSol = _population[fa].DeepClone();
            SolutionForThreeTree NewSol2 = _population[ma].DeepClone();
            NewSol.CrossOver(NewSol2);
            _spring.Add(NewSol);
            _spring.Add(NewSol2);
            NewSol.Eval();
            NewSol2.Eval();
        }
        void Mutation(SolutionForThreeTree Sol) {
           
            if (_rand.Next(100) < MT_R)
            {
                
                SolutionForThreeTree temp = Sol.DeepClone();
                temp.Mutation();
                temp.Eval();
                _spring.Add(temp);                
            }
        
        }
        void Eval(int index){
            _population[index].Eval();
        }
        void Survive(){
            Console.WriteLine("Survive Start");
            _population[0].Eval();
            _population.AddRange(_spring);
            _population.Sort((x, y) => { return -x.Score.CompareTo(y.Score); });
            _population.RemoveRange(POPULATION_SIZE, _population.Count - POPULATION_SIZE);
            _spring.Clear();
            RobotTool.GenerateCsCode("Gen" + _gen + "BestBot1", _population[0].Show());
            RobotTool.GenerateCsCode("Gen" + _gen + "BestBot2", _population[1].Show());
            RobotTool.GenerateCsCode("Gen" + _gen + "BestBot3", _population[2].Show());
            Console.WriteLine("BestScore:" + _population[0].Score);
            Console.WriteLine("Survive end");
        }
        void CheckBestSol() {
            
            for (int i = 0; i < _population.Count; i++) {
                SolutionForThreeTree S = _population[i];
                if (S.Score > _bestSol.Score) {
                    _bestSol = S;
                }
            }
            RobotTool.GenerateCsCode("BESTBOT", _bestSol.Show());
            RobotTool.CompileCsToDLL("BESTBOT");
            
        }

    }
}


